home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1997
/
MacHack 1997.toast
/
Hacks
/
Hacks ’96
/
MenuHack
/
MenuHackSource.sit
/
MHPlug.cp
< prev
next >
Wrap
Text File
|
1996-06-21
|
18KB
|
617 lines
#include "ArrangeCallbacks.h"
#include "PluginLibrary.h"
#include <Dialogs.h>
#include <StandardFile.h>
#include <string.h>
#include <Folders.h>
#include <TextUtils.h>
#include <Resources.h>
#include <AppleEvents.h>
#include <PLStringFuncs.h> /* some special string handling stuff */
#if defined (__MWERKS__) && defined (PLUGIN_GLOBALS)
#include "A4Stuff.h"
#endif
#define ModuleRsrcID -0x8000
#define SecondsInADay 86400
#define OurModuleID 0x70000048 // Unique module ID; replace with a value
// obtained from Common Knowledge.
#define baseCmdCode OurModuleID
#define aboutCmdCode (baseCmdCode + 0)
#define pluginName "MHPlug" // controls our name in the About menu
#if defined (__MWERKS__) && defined (PLUGIN_GLOBALS)
#define SetupA4 long lOldA4 = SetCurrentA4 ();
#define TearDownA4 SetA4 (lOldA4);
#define UnloadSegs UnloadSegsFunc ();
static void UnloadSegsFunc ();
#else
#define SetupA4
#define TearDownA4
#define UnloadSegs
#endif //__MWERKS__
typedef struct {
arDocumentPtr whichDoc;
const ArrangeCallbackTbl* calls;
} myprefs;
typedef struct {
arNoteID BookMarksTopic;
arNoteID ClippingTopic;
arNoteID InternetFolder;
arNoteID DataFolder;
arFieldID lParentSubnoteFieldID;
arFieldID lTopicContentsFieldID;
arFieldID lURLFieldID;
arFieldID lCommentID;
arFieldID lTitleField;
arNoteID lURLNoteID;
} sIDList;
static Boolean GetMyIDs(myprefs* theprefs, sIDList *theIDs)
{
char Internetname[16] = "Internet";
char CommentName[16] = "Comment";
char URLName[16] = "URL";
char ClippingTopicName[32] = "Clippings";
char TitleName[32] = "Title";
char BookTopicName[16] = "BookMarks";
theIDs->DataFolder = theprefs->calls->sysObj->GetBuiltInObject (boOrganizerFolder);
if (!theIDs->DataFolder)
{
return false;
}
theIDs->lParentSubnoteFieldID = theprefs->calls->sysObj->GetBuiltInObject (boSubnotesField);
if (!theIDs->lParentSubnoteFieldID)
{
return false;
}
theIDs->lTopicContentsFieldID = theprefs->calls->sysObj->GetBuiltInObject (boTopicContentsField);
if (!theIDs->lTopicContentsFieldID)
{
return false;
}
theIDs->lURLNoteID = theprefs->calls->sysObj->LookupObjectName ( arNoteType,(char*) (&URLName), true);
if (!theIDs->lURLNoteID)
{
theIDs->lURLNoteID = theprefs->calls->sysObj->CreateNoteType((char*)(&URLName), true);
if (!theIDs->lURLNoteID)
{
return false;
}
}
theIDs->lTitleField = theprefs->calls->sysObj->LookupObjectName ( arField,(char*) (&TitleName), true);
if (!theIDs->lTitleField)
{
theIDs->lTitleField = theprefs->calls->sysObj->CreateField((char*)(&TitleName), arFTText, true);
if (!theIDs->lTitleField)
{
return false;
}
}
if (!theprefs->calls->notes->NoteHasField(theIDs->lURLNoteID, theIDs->lTitleField, false))
{
theprefs->calls->notes->AddField(theIDs->lURLNoteID, theIDs->lTitleField, nil);
}
theIDs->lURLFieldID = theprefs->calls->sysObj->LookupObjectName ( arField,(char*) (&URLName), true);
if (!theIDs->lURLFieldID)
{
theIDs->lURLFieldID = theprefs->calls->sysObj->CreateField((char*)(&URLName), arFTText, true);
if (!theIDs->lURLFieldID)
{
return false;
}
}
theIDs->lCommentID = theprefs->calls->sysObj->LookupObjectName ( arField,(char*) (&CommentName), true);
if (!theIDs->lCommentID)
{
theIDs->lCommentID = theprefs->calls->sysObj->CreateField((char*)(&CommentName), arFTText, true);
if (!theIDs->lCommentID)
{
return false;
}
}
theIDs->InternetFolder = theprefs->calls->sysObj->LookupObjectName ( arFolder,(char*) (&Internetname), false);
if (!theIDs->InternetFolder)
{
theIDs->InternetFolder = theprefs->calls->sysObj->CreateFolder( (char*)(&Internetname), nil);
if (!theIDs->InternetFolder)
{
return false;
}
}
theIDs->ClippingTopic = theprefs->calls->sysObj->LookupObjectName ( arTopic,(char*) (&ClippingTopicName), false);
if (!theIDs->ClippingTopic)
{
theIDs->ClippingTopic = theprefs->calls->sysObj->CreateTopic( (char*)(&ClippingTopicName), theIDs->DataFolder, nil, false);
if (!theIDs->ClippingTopic)
{
return false;
}
}
theIDs->BookMarksTopic = theprefs->calls->sysObj->LookupObjectName ( arTopic,(char*) (&BookTopicName), false);
if (!theIDs->BookMarksTopic)
{
theIDs->BookMarksTopic = theprefs->calls->sysObj->CreateTopic( (char*)(&BookTopicName), theIDs->InternetFolder, nil, false);
if (!theIDs->BookMarksTopic)
{
return false;
}
}
return true;
}
void AppendNoteToField (const ArrangeCallbackTbl* calls, arNoteID lURLNoteID, arNoteID lParentNoteID, arFieldID lParentSubnoteFieldID)
{
Integer lInsertionIndex;
lInsertionIndex = calls->data->GetFieldListLen (lParentNoteID, lParentSubnoteFieldID);
calls->data->AddListFieldEntry (lParentNoteID, lParentSubnoteFieldID, lInsertionIndex, lURLNoteID, nullSFF);
calls->doc->SetChangedFlag ();
}
// This apple event gets the nth URL note in bookmarks
static pascal OSErr aeGetAURL(const AppleEvent *theAppleEvent,
AppleEvent *reply, myprefs* theprefs)
{
OSErr anErr;
OSType typeCode;
long thesize;
long whichURL;
arDocumentPtr tempDoc;
long lTopicCount;
arNoteID lCurNote;
sIDList theIDs;
Str255 tempstr;
unsigned long templong;
anErr = AEGetParamPtr(theAppleEvent, '----', typeLongInteger, &typeCode,
&whichURL, sizeof(whichURL), &thesize);
if (anErr!=noErr)
{
return anErr;
}
tempDoc = theprefs->calls->doc->GetCurrentDoc ();
theprefs->calls->doc->SetCurrentDoc(theprefs->whichDoc);
if (!GetMyIDs(theprefs, &theIDs))
{
//DebugStr("\pWhoops");
return -1;
}
lTopicCount = theprefs->calls->data->GetFieldListLen (theIDs.BookMarksTopic, theIDs.lTopicContentsFieldID);
anErr = AEPutParamPtr( reply, 'tCnt', typeLongInteger, (const void*)&lTopicCount, sizeof(lTopicCount) );
if ((whichURL>0) && (whichURL<=lTopicCount))
{
lCurNote = theprefs->calls->data->GetFieldListEntry (theIDs.BookMarksTopic, theIDs.lTopicContentsFieldID,whichURL-1);
thesize = theprefs->calls->data->GetFieldText(lCurNote, theIDs.lURLFieldID, 255, (char*)&tempstr[1]);
tempstr[0] = thesize;
anErr = AEPutParamPtr( reply, 'URL ', typeChar, (const void*)&tempstr[1], tempstr[0] );
if (anErr!=noErr)
{
return anErr;
}
thesize = theprefs->calls->data->GetFieldText(lCurNote, theIDs.lCommentID, 255, (char*)&tempstr[1]);
tempstr[0] = thesize;
anErr = AEPutParamPtr( reply, 'Comm', typeChar, (const void*)&tempstr[1], tempstr[0] );
if (anErr!=noErr)
{
return anErr;
}
thesize = theprefs->calls->data->GetFieldText(lCurNote, theIDs.lTitleField, 255, (char*)&tempstr[1]);
tempstr[0] = thesize;
anErr = AEPutParamPtr( reply, 'Titl', typeChar, (const void*)&tempstr[1], tempstr[0] );
if (anErr!=noErr)
{
return anErr;
}
}
theprefs->calls->doc->SetCurrentDoc(tempDoc);
return noErr;
}
static void InstallMe(myprefs* theprefs)
{
OSErr aevtErr;
aevtErr = AEInstallEventHandler( 'MHck', 'GtUr',(AEEventHandlerUPP)aeGetAURL, (long)theprefs, false ) ;
}
class Plugin
{
public:
Plugin(const ArrangeCallbackTbl* theCalls);
~Plugin();
arHookResult ClickNotify( arClickLocation loc, Point where, Short modifiers,
Short clickCount, arNoteID note, arFieldID field,
arPathID path );
arHookResult KeyNotify ( Short theChar, Short key, Short modifiers );
arHookResult MenuNotify ( Integer commandCode, Integer commandParam,
Short modifiers );
void AboutToMenu();
arHookResult FieldNotify( arNoteID note, arFieldID field, arFieldAction action,
const char* choiceText );
void TopicNotify( arTopicID newTopic, arWindowID newWindow,
arTopicAction action );
void TickNotify ( );
arHookResult FileNotify ( arFileAction action );
arHookResult QuitNotify ( );
void ATMNotify ( );
private:
void WriteANote ( arNoteID note);
void ReadANote ( );
const ArrangeCallbackTbl* calls; // callback table
myprefs theprefs;
}; // Plugin
/*************************************************************************/
/**************************** Main entry point ***************************/
/*************************************************************************/
/* Root entry point for the module - must be the first function in the
* file, so that the linker will place it first in the code segment.
* This is the routine which Arrange calls at application startup time
* (and again at quit time).
*
* Most plug-ins will not need to modify this routine or the Hook functions
* immediately following. All customization can be done in the "Plugin"
* section (below).
*/
extern "C" Integer main( ModuleParamBlock *pb, pModuleRootAction action,
Integer /*p1*/ )
{
SetupA4;
Integer lResult = 0;
/* Extract the callback table from our parameter block, and coerce it
* to the extended Arrange table.
*/
ArrangeCallbackTbl* calls = (ArrangeCallbackTbl*)(pb->calls);
Assert (sizeof (Byte) == 1);
Assert (sizeof (Short) == 2);
Assert (sizeof (Integer) == 4);
Assert (sizeof (Float) == 8);
switch (action)
{
case mrLoad:
{
// Allocate memory and create a new Plugin object.
void* storage = calls->mem->AllocMem( sizeof(Plugin),
amFreeStore | amErrIfNoMem );
pb->moduleRefcon = uInteger(new(storage) Plugin(calls));
lResult = 1;
}
break;
case mrUnload:
{
/* Dispose of the Plugin object and release the memory
* it occupies.
*/
delete (Plugin*)(pb->moduleRefcon);
calls->mem->DeallocMem((void*)(pb->moduleRefcon), amFreeStore);
lResult = 0;
}
break;
case mrFindEntry:
default:
{
/* This module contains no extended entry points, so we always
* return 0 for the mrFindEntry message. Most plug-ins will not
* use extended entry points.
*/
lResult = 0;
}
break;
} // switch (action)
TearDownA4;
return lResult;
} // main
#if defined (__MWERKS__) && defined (PLUGIN_GLOBALS)
#pragma segment PluginLib
static void PluginLibSeg (void)
{
}
#pragma segment Main
static void UnloadSegsFunc ()
{
// Put segment unloading code here.
// Do NOT use UnloadSeg!
//UnloadA4Seg (PluginLibSeg);
}
#endif //__MWERKS__ && PLUGIN_GLOBALS
/* These hook functions simply pass control to the appropriate function
* in the Plugin object.
*/
static arHookResult OurClickHook( ModuleParamBlock* pb, arClickLocation loc,
Point where, pShort modifiers, pShort clickCount,
arNoteID note, arFieldID field,
arPathID path ENDP )
{
SetupA4;
arHookResult eResult = ((Plugin*)(pb->moduleRefcon))->ClickNotify( loc, where, modifiers,
clickCount, note, field,
path );
TearDownA4;
return eResult;
}
static arHookResult OurKeyHook( ModuleParamBlock* pb, pShort theChar, pShort key,
pShort modifiers ENDP )
{
SetupA4;
arHookResult eResult = ((Plugin*)(pb->moduleRefcon))->KeyNotify(theChar, key, modifiers);
TearDownA4;
return eResult;
}
static arHookResult OurMenuHook( ModuleParamBlock* pb, Integer commandCode,
Integer commandParam, pShort modifiers ENDP )
{
SetupA4;
arHookResult eResult = ((Plugin*)(pb->moduleRefcon))->MenuNotify( commandCode, commandParam,
modifiers );
TearDownA4;
return eResult;
}
void OurATMHook(ModuleParamBlock* pb)
{
((Plugin*)(pb->moduleRefcon))->AboutToMenu();
} // OurATMHook
static arHookResult OurFieldHook( ModuleParamBlock* pb, arNoteID note,
arFieldID field, arFieldAction action,
const char* choiceText ENDP )
{
SetupA4;
arHookResult eResult = ((Plugin*)(pb->moduleRefcon))->FieldNotify( note, field, action,
choiceText );
TearDownA4;
return eResult;
}
static void OurTopicHook( ModuleParamBlock* pb, arTopicID newTopic,
arWindowID newWindow, arTopicAction action ENDP )
{
SetupA4;
((Plugin*)(pb->moduleRefcon))->TopicNotify(newTopic, newWindow, action);
TearDownA4;
}
static void OurTickHook(ModuleParamBlock* pb ENDP)
{
SetupA4;
((Plugin*)(pb->moduleRefcon))->TickNotify();
TearDownA4;
}
static arHookResult OurFileHook(ModuleParamBlock* pb, arFileAction action ENDP)
{
SetupA4;
arHookResult eResult = ((Plugin*)(pb->moduleRefcon))->FileNotify(action);
TearDownA4;
return eResult;
}
static arHookResult OurQuitHook(ModuleParamBlock* pb ENDP)
{
SetupA4;
arHookResult eResult = ((Plugin*)(pb->moduleRefcon))->QuitNotify();
TearDownA4;
return eResult;
}
/*************************************************************************/
/********************************* Plugin ********************************/
/*************************************************************************/
/* Construct a Plugin object. This is called once, from the OurModuleRoot
* function, when the module is initialized (i.e. at application startup time).
*/
Plugin::Plugin(const ArrangeCallbackTbl* theCalls)
{
short thelength;
arDocumentPtr theCurDoc;
arDocumentPtr thePrefDoc;
Str255 ourname;
Handle temph;
// Record the callback table for future use.
calls = theCalls;
/* These commands, if un-commented-out, register this plugin to be called
* by Arrange when various events occur. For example, if you un-comment-out
* the call to SetClickHook, then Plugin::ClickNotify will be called
* whenever the user clicks in any of the locations described in the
* arClickLocation enum.
*/
// calls->ui->SetClickHook(OurClickHook, 0, true);
// calls->ui->SetKeyHook (OurKeyHook, 0, true, charFilter, keyFilter, modFilter);
// calls->ui->SetMenuHook (OurMenuHook, 0, true, whichCommand);
// calls->ui->SetFieldHook(OurFieldHook, 0, true, whichField);
calls->ui->SetTopicHook(OurTopicHook, 0, true);
// calls->ui->SetFileHook (OurFileHook, 0, true);
// calls->ui->SetQuitHook (OurQuitHook, 0, true);
calls->ui->SetTickHook (OurTickHook, 0, true);
calls->ui->SetATMHook (OurATMHook, 0, true);
// Add an item to the About Plugins menu for this plugin.
temph = GetResource('STR ', ModuleRsrcID+1); // "About MenuHack"
BlockMove ((Ptr)*temph, (Ptr)(&ourname), 50);
ourname[ourname[0]+1] = 0; //Make good for C String
calls->ui->AddMenuItem(mPluginAbout, (char *)(&ourname[1]), 0, aboutCmdCode, 1);
calls->ui->SetMenuHook(OurMenuHook, 1, true, aboutCmdCode);
InstallMe(&theprefs);
} // Plugin constructor
/* Dispose of a Plugin object. This is called when Arrange terminates. You
* should free up any data structures which you allocation in the Plugin
* constructor (above).
*/
Plugin::~Plugin()
{
} // ~Plugin
/* This function is called whenever the user clicks in an "interesting place"
* in a document window. It should return true if we handle the click, false
* (the normal case) when the click should be passed along to other plug-ins
* or to Arrange's normal event processing. See the documentation for
* SetClickHook for more details.
*/
arHookResult Plugin::ClickNotify( arClickLocation loc, Point where,
Short modifiers, Short clickCount,
arNoteID note, arFieldID field,
arPathID path )
{
return false; // Let Arrange handle the event
} // ClickNotify
/* This function is called whenever the user types a key which matches the
* filters we pass to SetKeyHook in Plugin's constructor. It should return
* true if we handle the event, false (the normal case) when the event
* should be passed along to other plug-ins or to Arrange's normal event
* processing. See the documentation for SetKeyHook for more details.
*/
arHookResult Plugin::KeyNotify(Short theChar, Short key, Short modifiers)
{
return false; // Let Arrange handle the event
} // KeyNotify
/* This function is called whenever the user clicks in the menu bar or types
* a command key, just before processing the event. It does any fixing up
* of menus which might be necessary based on the current state of affairs.
*/
void Plugin::AboutToMenu()
{
}
arHookResult Plugin::MenuNotify( Integer commandCode, Integer commandParam,
Short modifiers )
{
/* If this is our About menu item, display our about-box dialog and return
* true to indicate we handled the command.
*/
if (commandCode == aboutCmdCode)
{
Alert(ModuleRsrcID, nil);
return true;
}
return false; // Let Arrange handle the event
} // MenuNotify
/* If we register to recieve events for a field by calling SetFieldHook, this
* function will be called for any events in that field. It should return
* false in almost all cases. See the documentation for SetFieldHook for
* more details.
*/
arHookResult Plugin::FieldNotify( arNoteID note, arFieldID field,
arFieldAction action, const char* choiceText )
{
return false;
} // FieldNotify
/* This function is called whenever the user switches windows or changes
* the current folder/topic/view in the front window. See the documentation
* for SetTopicHook for more details.
*/
void Plugin::TopicNotify( arTopicID newTopic, arWindowID newWindow,
arTopicAction action )
{
theprefs.calls = calls;
theprefs.whichDoc = calls->doc->GetCurrentDoc ();
} // TopicNotify
/* This function is called periodically, whenever Arrange recieves a null
* event from the Event Manager.
*/
void Plugin::TickNotify()
{
OSErr err;
} // TickNotify
/* This function is called whenever various file-level events occur. It
* should return true in almost all cases. See the documentation for
* SetFileHook for more details.
*/
arHookResult Plugin::FileNotify(arFileAction action)
{
return true;
} // FileNotify
/* This function is called if the user voluntarily quits Arrange. It should
* return true to allow the user to quit, false to prevent it. See the
* documentation for SetQuitHook for more details.
*/
arHookResult Plugin::QuitNotify()
{
return true;
} // QuitNotify
/* This function is called whenever the user clicks in the menu bar or types
* a command key, just before processing the event. It should do any fixing
* up of menus which might be necessary based on the current state of affairs.
* See the documentation for SetATMHook for more details.
*/
void Plugin::ATMNotify()
{
} // ATMNotify